﻿using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.IO;
using System.Text;

namespace Jaiden.Data.Exporting
{
    public class CsvExportTarget : IExportTarget
    {
        private readonly Stream _stream;

        public CsvExportTarget(Stream stream)
        {
            _stream = stream;
        }

        public CsvExportTarget(string path) : this(File.Open(path, FileMode.Create))
        {
        }

        public Encoding Encoding { get; set; } = Encoding.UTF8;

        public void Export<T>(IEnumerable<T> data, IExportRole<T> role)
        {
            using (var sw = new StreamWriter(_stream, Encoding))
            {
                for (var i = 0; i < role.Columns.Count; i++)
                {
                    if (i > 0)
                    {
                        sw.Write(',');
                    }

                    WriteField(sw, role.Columns[i].Name);
                }

                sw.WriteLine();
                foreach (var item in data)
                {
                    for (var i = 0; i < role.Columns.Count; i++)
                    {
                        if (i > 0)
                        {
                            sw.Write(',');
                        }

                        WriteField(sw, role.Columns[i].ValueGetter(item));
                    }

                    sw.WriteLine();
                }
            }
        }

        /// <summary>
        ///     Converts a value to how it should output in a csv file
        ///     If it has a comma, it needs surrounding with double quotes
        ///     Eg Sydney, Australia -> "Sydney, Australia"
        ///     Also if it contains any double quotes ("), then they need to be replaced with quad quotes[sic] ("")
        ///     Eg "Dangerous Dan" McGrew -> """Dangerous Dan"" McGrew"
        /// </summary>
        /// <param name="value"></param>
        /// <param name="columnSeparator">
        ///     The string used to separate columns in the output.
        ///     By default this is a comma so that the generated output is a CSV document.
        /// </param>
        private string MakeValueCsvFriendly(object value, string columnSeparator = ",")
        {
            if (value == null)
            {
                return "";
            }

            if (value is INullable nullable && nullable.IsNull)
            {
                return "";
            }

            if (value is DateTime time)
            {
                if (Convert.ToInt32(time.TimeOfDay.TotalSeconds) == 0)
                {
                    return time.ToString("yyyy-MM-dd");
                }

                return time.ToString("yyyy-MM-dd HH:mm:ss");
            }

            var output = value.ToString().Trim();

            if (output.Length > 30000) //cropping value for stupid Excel
            {
                output = output.Substring(0, 30000);
            }

            if (output.Contains(columnSeparator) || output.Contains("\"") || output.Contains("\n") ||
                output.Contains("\r"))
            {
                output = '"' + output.Replace("\"", "\"\"") + '"';
            }

            return output;
        }

        protected void WriteField(StreamWriter writer, object field)
        {
            var value = MakeValueCsvFriendly(field);
            writer.Write(value);
        }
    }
}